#include "cellNetwork.h"
#include <string.h>

#define CELLNETWORK_HEADER "#include \"cellNetwork.h\""

CellPosition FindMaxPosition(CellArray *cells, int node_number)
{
	CellPosition maxPosition;
	
	maxPosition.x = cells[0].position.x;
	maxPosition.y = cells[0].position.y;

	for (int i = 1; i < node_number; i++)
	{
	    if (cells[i].position.x > maxPosition.x)
	    {
		maxPosition.x = cells[i].position.x;
	    }

	    if (cells[i].position.y > maxPosition.y)
	    {
		maxPosition.y = cells[i].position.y;
	    }
	}

	return maxPosition;
}

/**
*Find the top-left point of the physical process.
*/

CellPosition FindMinPosition(CellArray *cells, int node_number)
{
	CellPosition minPosition;
	
	minPosition.x = cells[0].position.x;
	minPosition.y = cells[0].position.y;

	for (int i = 1; i < node_number; i++)
	{
	    if (cells[i].position.x < minPosition.x)
	    {
		minPosition.x = cells[i].position.x;
	    }

	    if (cells[i].position.y < minPosition.y)
	    {
		minPosition.y = cells[i].position.y;
	    }
	}
	return minPosition;
}

/**
*Finding the bottom-right point of the physical process.
*/

int FindIndexPosition(CellPosition cell, CellArray *cells, int node_number)
{
	int index = -1;
	
	for (int i = 0; i < node_number; i++)
	{
	    if (cells[i].position.x == cell.x && cells[i].position.y == cell.y)
	    {
		index = i;
	 	break;
	    }
	}
	return index;
}

/**
* Finding the west (left)  neighbour. If x < 0 return -1 ortherwise return index of Position (x-1, y)
*/

int West(CellPosition cell, CellPosition min_pos, CellArray *cells, int node_number)
{
	int index = -1;	

	if (cell.x > min_pos.x)
	{
	   cell.x = cell.x - 1;
	   cell.y = cell.y;
	   index = FindIndexPosition(cell, cells, node_number);
	}

	return index;
}

/**
* Finding the East (right) neighbour. If x > maxPosition.x return -1 ortherwise return index of Position (x+1, y)
*/
int East(CellPosition cell, CellPosition max_pos, CellArray *cells, int node_number)
{
	int index = -1;
	
	if (cell.x < max_pos.x)
	{
	    cell.x = cell.x + 1;
	    cell.y = cell.y;
	    index = FindIndexPosition(cell, cells, node_number);
	}

	return index;
}
/**
* Finding the North (above) neighbour. If y < 0 return -1 ortherwise return index of Position (x, y-1)
*/
int North(CellPosition cell, CellPosition min_pos, CellArray *cells, int node_number)
{
	int index = -1;
	
	if (cell.y > min_pos.y)
	{
	    cell.x = cell.x;
	    cell.y = cell.y - 1;
	    index = FindIndexPosition(cell, cells, node_number);
	}
	return index;
}
/**
* Finding the South (left) neighbour. If y > maxPosition.y return -1 ortherwise return index of Position (x, y+1)
*/
int South(CellPosition cell, CellPosition max_pos, CellArray *cells, int node_number)
{
	int index = -1;
	
	if (cell.y < max_pos.y)
	{
	    cell.x = cell.x;
	    cell.y = cell.y + 1;
	    index = FindIndexPosition(cell, cells, node_number);
	}
	return index;
}

/**
*West_North (Left_Above)
*/

int West_North(CellPosition cell, CellPosition min_pos, CellPosition max_pos, CellArray *cells, int node_number)
{
	int index = -1;
	
	if (cell.x > min_pos.x && cell.y > min_pos.y)
	{
	    cell.x = cell.x - 1;
	    cell.y = cell.y - 1;
	    index = FindIndexPosition(cell, cells, node_number);
	}
	return index;
}

/**
*West_South (Left_Below)
*/
int West_South(CellPosition cell, CellPosition min_pos, CellPosition max_pos, CellArray *cells, int node_number)
{
	int index = -1;
	
	if (cell.x > min_pos.x && cell.y < max_pos.y)
	{
	    cell.x = cell.x - 1;
	    cell.y = cell.y + 1;
	    index = FindIndexPosition(cell, cells, node_number);
	}
	return index;
}

/**
*East_North (Right_Above)
*/
int East_North(CellPosition cell, CellPosition min_pos, CellPosition max_pos, CellArray *cells, int node_number)
{
	int index = -1;
	
	if (cell.x < max_pos.x && cell.y > min_pos.y)
	{
	    cell.x = cell.x + 1;
	    cell.y = cell.y - 1;
	    index = FindIndexPosition(cell, cells, node_number);
	}
	return index;
}

/**
*East_South (Right_Below)
*/
int East_South(CellPosition cell, CellPosition min_pos, CellPosition max_pos, CellArray *cells, int node_number)
{
	int index = -1;
	
	if (cell.x < max_pos.x && cell.y < max_pos.y)
	{
	    cell.x = cell.x + 1;
	    cell.y = cell.y + 1;
	    index = FindIndexPosition(cell, cells, node_number);
	}
	return index;
}

void GenerateNetVonNeumann(char *fileName, CellArray *cells, int node_number)
{
	CellPosition maxPosition, cell, minPosition;
	int index, west, east, north, south;
	int nbOut = 0, nbIn, nbDyn = 5;
	FILE *f;
	//int height, width;	
	maxPosition = FindMaxPosition(cells, node_number);
	minPosition = FindMinPosition(cells, node_number);
	
	//height = maxPosition.y - minPosition.y;
	//width = maxPosition.x - minPosition.x;

	f = fopen(fileName, "w");

	if (f == NULL)
	{
	   printf("\nGenerateNetwork: Can not open a file named %s!\n", fileName);
	   return;
	}
	fprintf(f, "%s", CELLNETWORK_HEADER);
	fprintf(f," \n\n");
	fprintf(f, "canaux channels_h[] = \n{ \n");

	FileOfId file;

	for (index = 0; index < node_number; index++)
	{
	    nbOut = 0;
	    ResetFile_h(file);
	    cell.x = cells[index].position.x;
	    cell.y = cells[index].position.y; 
	    //index = FindIndexPosition(cell, cells, node_number);
	    //printf("%-3d", index);
	    //Finding neighbours
	    west = West(cell, minPosition, cells, node_number);
	    east = East(cell, maxPosition, cells, node_number);
	    north = North(cell, minPosition, cells, node_number);
	    south = South(cell, maxPosition, cells, node_number);

	    //Holding direction of neighbors
	    Direction directs[100];
 	    int d = 0;
	    if (west != -1) 
	    {
		AddIdInFile_h(west, file); 
		directs[d].x = -1;
		directs[d].y = 0;
		d++;
	    }
	    if (east != -1) 
	    {
		AddIdInFile_h(east, file);
		directs[d].x = 1;
		directs[d].y = 0;
		d++;
	    }
	    if (north != -1) 
	    {
		AddIdInFile_h(north, file);
		directs[d].x = 0;
		directs[d].y = -1;
		d++;
	    }
	    if (south != -1) 
	    {
		AddIdInFile_h(south, file);
		directs[d].x = 0;
		directs[d].y = 1;
		//d++;
	    }

 	    nbOut = file.limit;
	    nbIn = nbOut;

	    fprintf(f, "\t{%d,%d,%d,{", nbOut, nbIn, nbDyn);//index
	    /*
	    for (int i = 0; i < nbIn; i++)
	    {
	   	fprintf(f, "{%d,%d}", index, i);
		if (i < nbOut - 1)
		{
		   fprintf(f, ",");
		}
	    }
	    */
	    fprintf(f, "},{");

	    for (int i = 0; i < file.limit; i++)
	    {
		fprintf(f, "{%d,%d,{%d,%d}}", file.newFound[i], i, directs[i].x, directs[i].y);
		if (i < file.limit - 1)
		{
		   fprintf(f, ",");
		}
	    }

	    fprintf(f, "},{},{}}");

	    if (index < node_number - 1) 
	    { 
		fprintf(f, ",\n");
	    }
	    else
	    {
		fprintf(f, "\n");
	    }
	}
	
	fprintf(f, "};");	
	fclose(f);
}

void GenerateNetMoore(char *fileName, CellArray *cells, int node_number)
{
	CellPosition maxPosition, cell, minPosition;
	int index, west, east, north, south, west_north, west_south, east_north, east_south;
	int nbOut = 0, nbIn, nbDyn = 5;
	FILE *f;
	//int height, width;	
	maxPosition = FindMaxPosition(cells, node_number);
	minPosition = FindMinPosition(cells, node_number);
	
	f = fopen(fileName, "w");

	if (f == NULL)
	{
	   printf("\nGenerateNetwork: Can not open a file named %s!\n", fileName);
	   return;
	}
	fprintf(f, "%s", CELLNETWORK_HEADER);
	fprintf(f," \n\n");
	fprintf(f, "canaux channels_h[] = \n{ \n");

	FileOfId file;

	for (index = 0; index < node_number; index++)
	{
	    nbOut = 0;
	    ResetFile_h(file);
	    cell.x = cells[index].position.x;
	    cell.y = cells[index].position.y; 
	    //Finding neighbours
	    west = West(cell, minPosition, cells, node_number);
	    east = East(cell, maxPosition, cells, node_number);
	    north = North(cell, minPosition, cells, node_number);
	    south = South(cell, maxPosition, cells, node_number);
	    west_north =  West_North(cell, minPosition, maxPosition, cells, node_number);
	    west_south =  West_South(cell, minPosition, maxPosition, cells, node_number);
	    east_north =  East_North(cell, minPosition, maxPosition, cells, node_number);
	    east_south =  East_South(cell, minPosition, maxPosition, cells, node_number);
	    //Holding direction of neighbors
	    Direction directs[100];
 	    int d = 0;
	    if (west != -1) 
	    {
		AddIdInFile_h(west, file); 
		directs[d].x = -1;
		directs[d].y = 0;
		d++;
	    }
	    if (east != -1) 
	    {
		AddIdInFile_h(east, file);
		directs[d].x = 1;
		directs[d].y = 0;
		d++;
	    }
	    if (north != -1) 
	    {
		AddIdInFile_h(north, file);
		directs[d].x = 0;
		directs[d].y = -1;
		d++;
	    }
	    if (south != -1) 
	    {
		AddIdInFile_h(south, file);
		directs[d].x = 0;
		directs[d].y = 1;
		d++;
	    }
	    if (west_north != -1) 
	    {
		AddIdInFile_h(west_north, file);
		directs[d].x = -1;
		directs[d].y = -1;
		d++;
	    } 
	    if (west_south != -1) 
	    {
		AddIdInFile_h(west_south, file);
		directs[d].x = -1;
		directs[d].y = 1;
		d++;
	    } 
	    if (east_north != -1) 
	    {
		AddIdInFile_h(east_north, file);
		directs[d].x = 1;
		directs[d].y = -1;
		d++;
	    } 
	    if (east_south != -1) 
	    {
		AddIdInFile_h(east_south, file);
		directs[d].x = 1;
		directs[d].y = 1;
		d++;
	    }

 	    nbOut = file.limit;
	    nbIn = nbOut;

	    fprintf(f, "\t{%d,%d,%d,{", nbOut, nbIn, nbDyn);//index
	    /*
	    for (int i = 0; i < nbIn; i++)
	    {
	   	fprintf(f, "{%d,%d}", index, i);
		if (i < nbOut - 1)
		{
		   fprintf(f, ",");
		}
	    }
	    */
	    fprintf(f, "},{");

	    for (int i = 0; i < file.limit; i++)
	    {
		fprintf(f, "{%d,%d,{%d,%d}}", file.newFound[i], i, directs[i].x, directs[i].y);
		if (i < file.limit - 1)
		{
		   fprintf(f, ",");
		}
	    }

	    fprintf(f, "},{},{}}");

	    if (index < node_number - 1) 
	    { 
		fprintf(f, ",\n");
	    }
	    else
	    {
		fprintf(f, "\n");
	    }
	}
	  //printf("\n");
	
	fprintf(f, "};");	
	fclose(f);
}


/**
*This function allows to generate network from data generated from NetGen tool. This version support two patterns: 
* @pattern: (vonneumann;moore)
*/
void GenerateNetwork(char *fileName, char *pattern, CellArray *cells, int node_number)
{
	if (strcmp(pattern, "vonneumann") == 0)
	{
	   GenerateNetVonNeumann(fileName, cells, node_number);		
	}
	else if (strcmp(pattern, "moore") == 0)
	{
	   GenerateNetMoore(fileName, cells, node_number);		
	}
	else
	{
	    printf("\nWarning: %s pattern is not supported in this version. The default pattern (Vonneumann) was generated.\n", pattern);
	   GenerateNetVonNeumann(fileName, cells, node_number);		
	}
}


